package edu.se.jai.websocketgateway.controller;

import edu.se.jai.dubboapi.proto.GameServiceProto.*;
import edu.se.jai.dubboapi.service.GameServiceApi;
import edu.se.jai.websocketgateway.dto.WebSocketMessage;
import edu.se.jai.websocketgateway.service.MessageSendService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;

import java.util.List;

/**
 * WebSocket控制器（Dubbo版）
 *
 * 负责处理所有WebSocket消息，通过 Dubbo3 Triple 协议调用 `game-service`：
 * - 玩家连接与房间管理
 * - 房间生命周期控制（创建、加入、离开、解散）
 * - 游戏流程控制（开始、停止、猜数）
 * - 消息路由与状态广播
 *
 * 消息路由规则：
 * - `/app/*`：客户端发送的全局操作
 * - `/room/{roomId}`：客户端发送的房间内操作
 * - `/user/queue/response`：服务器点对点反馈
 * - `/room/{roomId}`：服务器向房间广播
 * - `/topic/roomUpdate`：服务器向所有在线玩家广播房间列表
 */
@Slf4j
@Controller
@RequiredArgsConstructor
public class WebSocketController {

    @DubboReference(interfaceClass = GameServiceApi.class, protocol = "tri", check = false)
    private GameServiceApi gameServiceClient;
    
    private final MessageSendService messageSendService;

    /**
     * 处理玩家连接请求
     */
    @MessageMapping("/connect")
    public void handleConnect(@Payload WebSocketMessage message, SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        String playerName = (String)message.getData();

        log.info("玩家 {} 尝试连接，Session ID: {}", playerName, sessionId);

        // 通过Dubbo调用game-service注册玩家会话
        RegisterPlayerRequest request = RegisterPlayerRequest.newBuilder()
                .setSessionId(sessionId)
                .setPlayerName(playerName)
                .build();

        Result protoResult = gameServiceClient.registerPlayer(request);
        
        if (protoResult.getSuccess()) {
            messageSendService.notifyConnect(playerName, sessionId, true, "连接成功");
        } else {
            messageSendService.notifyConnect(playerName, sessionId, false, protoResult.getErrorMessage());
        }
    }

    /**
     * 处理创建房间请求
     */
    @MessageMapping("/create")
    public void handleCreateRoom(SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        log.info("收到创建房间请求: sessionId={}", sessionId);

        SessionRequest request = SessionRequest.newBuilder()
                .setSessionId(sessionId)
                .build();

        Result protoResult = gameServiceClient.createRoom(request);

        if (protoResult.getSuccess()) {
            GameRoomDto room = protoResult.getGameRoomDto();
            messageSendService.notifyCreate(room, sessionId);
        } else {
            log.error("创建房间失败: {}", protoResult.getErrorMessage());
            messageSendService.notifyErrorMessage(sessionId, protoResult.getErrorMessage());
        }
    }

    /**
     * 处理加入房间请求
     */
    @MessageMapping("/join")
    public void handleJoinRoom(@Payload WebSocketMessage message, SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        String roomId = (String)message.getData();
        
        log.info("收到加入房间请求: roomId={}, sessionId={}", roomId, sessionId);

        SessionRoomRequest request = SessionRoomRequest.newBuilder()
                .setSessionId(sessionId)
                .setRoomId(roomId)
                .build();

        Result protoResult = gameServiceClient.joinRoom(request);

        if (protoResult.getSuccess()) {
            GameRoomDto room = protoResult.getGameRoomDto();
            messageSendService.notifyJoin(room, sessionId);
        } else {
            log.error("加入房间失败: {}", protoResult.getErrorMessage());
            messageSendService.notifyErrorMessage(sessionId, protoResult.getErrorMessage());
        }
    }

    /**
     * 处理房间内消息（解散房间、离开房间、游戏操作等）
     */
    @MessageMapping("/room/{roomId}")
    public void handleRoomMessage(@Payload WebSocketMessage message,
                                 @DestinationVariable String roomId,
                                 SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        log.info("收到房间消息: type={}, roomId={}, sessionId={}", message.getType(), roomId, sessionId);
        
        switch (message.getType()) {
            case DISSOLVE:
                SessionRoomRequest dissolveRequest = null;
                //todo 练习二 1.组织 dissolveRequest


                Result dissolveProtoResult =null;
                // todo 练习二 2.调用 dubbo 的解散房间接口，并且赋值到  dissolveProtoResult


                if (dissolveProtoResult.getSuccess()) {
                    GameRoomDto room = dissolveProtoResult.getGameRoomDto();
                    messageSendService.notifyRoomDissolved(room);
                } else {
                    log.error("解散房间失败: {}", dissolveProtoResult.getErrorMessage());
                    messageSendService.notifyErrorMessage(sessionId, dissolveProtoResult.getErrorMessage());
                }
                break;
                
            case LEAVE:
                SessionRoomRequest leaveRequest = null;
                //todo 练习二 1.组织 leaveRequest

                Result leaveProtoResult = null;
                // todo 练习二 2.调用 dubbo 的离开房间接口，并且赋值到  leaveProtoResult

                if (leaveProtoResult.getSuccess()) {
                    GameRoomDto room = leaveProtoResult.getGameRoomDto();
                    messageSendService.notifyLeave(room, sessionId);
                } else {
                    log.error("离开房间失败: {}", leaveProtoResult.getErrorMessage());
                    messageSendService.notifyErrorMessage(sessionId, leaveProtoResult.getErrorMessage());
                }
                break;

            case START_GAME:
                SessionRoomRequest startRequest = null;
                //todo 练习三 1.组织 startRequest

                Result startProtoResult = null;
                // todo 练习三 2.调用 dubbo 的开始游戏接口，并且赋值到  startProtoResult

                if (startProtoResult.getSuccess()) {
                    GameRoomDto room = startProtoResult.getGameRoomDto();
                    messageSendService.notifyGameStart(room);
                } else {
                    log.error("开始游戏失败: {}", startProtoResult.getErrorMessage());
                    messageSendService.notifyErrorMessage(sessionId, startProtoResult.getErrorMessage());
                }
                break;

            case STOP_GAME:
                SessionRoomRequest stopRequest = null;
                //todo 练习三 1.组织 stopRequest


                Result stopProtoResult = null;
                // todo 练习三 2.调用 dubbo 的停止游戏接口，并且赋值到  stopProtoResult

                if (stopProtoResult.getSuccess()) {
                    GameRoomDto room = stopProtoResult.getGameRoomDto();
                    messageSendService.notifyGameStop(room);
                } else {
                    log.error("停止游戏失败: {}", stopProtoResult.getErrorMessage());
                    messageSendService.notifyErrorMessage(sessionId, stopProtoResult.getErrorMessage());
                }
                break;

            case GUESS:
                Object dataObj = message.getData();
                Integer guess = (Integer) dataObj;

                if (guess == null) {
                    messageSendService.notifyErrorMessage(sessionId, "猜测数字不能为空");
                    return;
                }

                GuessDto guessRequest = null;
                // todo 练习四 1.组织 guessRequest
                
                Result guessProtoResult = null;
                // todo 练习四 2.调用 dubbo 的猜数字接口，并且赋值到  guessProtoResult

                if (guessProtoResult.getSuccess()) {
                    GuessResultDto resultData = guessProtoResult.getGuessResultDto();
                    String playerName = resultData.getPlayerName();
                    Boolean result = resultData.getResult();
                    String hint = resultData.getHint();

                    messageSendService.notifyGuessResult(roomId, playerName, guess, result, hint, sessionId);
                } else {
                    log.error("猜测失败: {}", guessProtoResult.getErrorMessage());
                    messageSendService.notifyErrorMessage(sessionId, guessProtoResult.getErrorMessage());
                }
                break;
                
            default:
                log.warn("未知的房间消息类型: {}", message.getType());
                messageSendService.notifyErrorMessage(sessionId, "未知的消息类型: " + message.getType());
        }
    }

    /**
     * 处理获取所有房间列表请求
     */
    @MessageMapping("/allRooms")
    public void handleGetAllRooms(SimpMessageHeaderAccessor headerAccessor) {
        String sessionId = headerAccessor.getSessionId();
        log.info("获取所有房间列表请求，会话ID: {}", sessionId);
        
        String requestSessionId = sessionId != null ? sessionId : "0";

        AllRoomsRequest request = AllRoomsRequest.newBuilder()
                .setSessionId(requestSessionId)
                .build();

        Result protoResult = gameServiceClient.getAllRooms(request);
        
        if (!protoResult.getSuccess() || protoResult.getGameRoomList() == null) {
            log.warn("获取房间列表失败");
            messageSendService.notifyErrorMessage(sessionId, "获取房间列表失败");
            return;
        }
        
        List<GameRoomDto> allRooms = protoResult.getGameRoomList().getRoomsList();
        log.info("找到 {} 个房间", allRooms.size());
        messageSendService.notifyAllRooms(allRooms, sessionId);
    }
}

